package net.sureon.common.security;

import java.util.HashMap;
import java.util.Map;

import net.sureon.app.AppContext;
import net.sureon.biz.CategoryAble;

/**
 * the action defines the mapping between user actions and required privileges.
 * 
 * @author jason
 * 
 */
public class Action {
	public static final Action ReadUser, WriteUser, ReadGroup, WriteGroup,
			ReadCompany, WriteCompany, WriteSystem;
	public static Map<String, Action> actionMap = new HashMap<String, Action>();

	public static Action getAction(String name) {
		return actionMap.get(name);
	}

	static {
		ReadUser = new Action("accessUserPage", new SimpleOperator(
				Privilege.USER_R));
		actionMap.put(ReadUser.getName(), ReadUser);
		WriteUser = new Action("updateUserPage", new OrOperator(
				new SimpleOperator(Privilege.USER_W), new SimpleOperator(
						Privilege.GROUP_W), new SimpleOperator(
						Privilege.COMPANY_W)));
		actionMap.put(WriteUser.getName(), WriteUser);
		ReadGroup = new Action("accessGroupAdminOnlyPage", new OrOperator(
				new SimpleOperator(Privilege.GROUP_R),new SimpleOperator(Privilege.GROUP_W), new SimpleOperator(
						Privilege.COMPANY_W)));
		actionMap.put(ReadGroup.getName(), ReadGroup);
		WriteGroup = new Action("updateGroupPage", new OrOperator(
				new SimpleOperator(Privilege.GROUP_W), new SimpleOperator(
						Privilege.COMPANY_W)));
		actionMap.put(WriteGroup.getName(), WriteGroup);
		ReadCompany = new Action("accessCompanyAdminOnlyPage",
				new SimpleOperator(Privilege.COMPANY_R));
		actionMap.put(ReadCompany.getName(), ReadCompany);
		WriteCompany = new Action("updateCompanyPage",
				new SimpleOperator(Privilege.COMPANY_W));
		actionMap.put(WriteCompany.getName(), WriteCompany);
		
		WriteSystem = new Action("writeSystem",
            new SimpleOperator(Privilege.SYSTEM_W));
		actionMap.put(WriteSystem.getName(), WriteSystem);
	}
	private final String name;
	private final PrivOperator requiredPriv;

	private Action(String name, PrivOperator requiredPriv) {
		this.name = name;
		this.requiredPriv = requiredPriv;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	public boolean probeAccess(CategoryAble target) {
		if(AppContext.isRootLogin()){
		    return true;
		}
	    return requiredPriv.isSatisfied(target);
	}

	private abstract static class PrivOperator {
		protected PrivOperator priv1;
		protected PrivOperator priv2;
		protected PrivOperator priv3;

		protected PrivOperator() {
		}

		protected PrivOperator(PrivOperator priv1, PrivOperator priv2) {
			this(priv1, priv2, null);
		}

		protected PrivOperator(PrivOperator priv1, PrivOperator priv2,
				PrivOperator priv3) {
			this.priv1 = priv1;
			this.priv2 = priv2;
			this.priv3 = priv3;
		}

		abstract protected boolean isSatisfied(CategoryAble target);
	}

	private static class SimpleOperator extends PrivOperator {
		private Privilege simplePriv;

		protected SimpleOperator(Privilege priv1) {
			this.simplePriv = priv1;
		}

		protected boolean isSatisfied(CategoryAble target) {
		    Role role = AppContext.getRole();
		    if(role != null){
		        if(!role.isPrivInRole(simplePriv)){
		            return false;
		        }
    			if (simplePriv.getCategory() == Privilege.USER_CATEGORY) {
    				return target.getUserID() == AppContext.getUserID();
    			}
    			if (simplePriv.getCategory() == Privilege.GROUP_CATEGORY) {
    				return target.getGroupID() == AppContext.getGroupID();
    			}
    			if (simplePriv.getCategory() == Privilege.COMPANY_CATEGORY) {
    				return target.getCompanyID() == AppContext.getCompanyID();
    			}
		    }
			return false;
		}
	}

	private static class OrOperator extends PrivOperator {
		protected OrOperator(PrivOperator priv1, PrivOperator priv2) {
			super(priv1, priv2);
		}

		protected OrOperator(PrivOperator priv1, PrivOperator priv2,
				PrivOperator priv3) {
			super(priv1, priv2, priv3);
		}

		protected boolean isSatisfied(CategoryAble target) {
			if (priv1 != null) {
				if (priv1.isSatisfied(target)) {
					return true;
				}
			}
			if (priv2 != null) {
				if (priv2.isSatisfied(target)) {
					return true;
				}
			}
			if (priv3 != null) {
				if (priv3.isSatisfied(target)) {
					return true;
				}
			}
			return false;
		}
	}

}
